<h1 id="custombehavior">Custom behavior</h1>

<p>This section summarizes Duktape behavior which deviates from the E5.1 or
other relevant specifications.</p>

<h2>Duktape built-in and custom types</h2>

<p>The <code>Duktape</code> built-in is (of course) non-standard and provides
access to Duktape specific features.  Also the buffer, pointer, and lightfunc
types are custom.</p>

<h2>Internal properties</h2>

<p>Objects may have <a href="#internalproperties">internal properties</a> which
are essentially hidden from normal code: they won't be enumerated or returned
even by e.g. <code>Object.getOwnPropertyNames()</code>.  Ordinary Ecmascript
code cannot refer to such properties because the property keys intentionally
use invalid UTF-8 (<code>0xFF</code> prefix byte).</p>

<h2>"use duk notail" directive</h2>

<p>The <a href="#use-duk-notail">"use duk notail"</a> directive is non-standard.
It prevents a function from being tail called.</p>

<h2>The global require() function for module loading</h2>

<p>The <code>require()</code> built-in is non-standard, and provided for
CommonJS-based module loading, see <a href="#modules">Modules</a>.</p>

<h2>Additional Error and Function object properties</h2>

<p>See
<a href="#errorobjects">Error objects</a> and
<a href="functionobjects">Function objects</a>.</p>

<p>Non-strict function instances don't have a <code>caller</code> property in the
E5/E5.1 specification.  Some real world code expects to have this property, so it
can be enabled with the feature option <code>DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY</code>.</p>

<h2>Function statements</h2>

<p>E5.1 does not allow a function declaration to appear outside program or
function top level:</p>
<pre class="ecmascript-code">
function test() {
    // point A
    try {
        throw new Error('test');
    } catch (e) {
        // This is a SyntaxError in E5.1
        function func() {
            print(typeof e);
        }
        // point B
    }
    // point C
}
</pre>

<p>These declarations are also referred to as "function statements", and appear
quite often in real world code (including the test262 test suite), so they are
allowed by Duktape.  Unfortunately there are several semantics used by different
Javascript engines.  Duktape follows the V8 behavior for function statements:</p>
<ul>
<li>Strict function: a SyntaxError is thrown (standard behavior).</li>
<li>Non-strict function: treat a function statement like an ordinary function
    declaration, conceptually "hoisting" it to the top of the function.</li>
</ul>

<p>As an illustration, the above example would behave as the following:</p>
<pre class="ecmascript-code">
function test() {
    function func() {
        print(typeof e);
    }
 
    try {
        throw new Error('test');
    } catch (e) {
    }
}
</pre>

<p><code>func()</code> in the above example would already be declared
and callable in point A, and would not have access to the <code>e</code>
binding in any of the points A, B, or C.</p>

<h2>RegExp leniency</h2>

<p>Although not allowed by E5.1, the following escape is allowed in RegExp
syntax:</p>
<pre>
  /\$/       /* matches dollar literally, non-standard */
  /\u0024/   /* same, standard */
</pre>

<p>This escape occurs in real world code so it is allowed.  (More leniency
will be added in future versions to deal with real world RegExps; dollar
escapes are not the only issue.)</p>

<h2>Array.prototype.splice() when deleteCount not given</h2>

<p>When deleteCount (the 2nd argument) is not given to <code>Array.prototype.splice()</code>,
the standard behavior is to work as if the 2nd argument was <code>undefined</code>
(or 0, which has the same behavior after coercions).  A more real world compatible
behavior is to treat the missing argument like positive infinity, i.e. to extend
the splice operation to the end of the array.</p>

<p>Because the non-standard real world behavior is expected by much existing code,
Duktape uses this behavior by default.  The strict standards compliant behavior can
be enabled with the feature option <code>DUK_OPT_NO_NONSTD_ARRAY_SPLICE_DELCOUNT</code>.</p>

<h2>Array.prototype.concat() trailing non-existent elements</h2>

<p>When the result of an array <code>concat()</code> would have trailing non-existent
elements, the standard behavior is to ignore them so that they are not reflected in
the result <code>length</code>.  Real world behavior is to include them in the result
value <code>length</code>.  See
<a href="https://github.com/svaarala/duktape/blob/master/tests/ecmascript/test-bi-array-proto-concat-nonstd-trailing.js">test-bi-array-proto-concat-nonstd-trailing.js</a>.</p>

<p>The real world behavior seems consistent in other engines (V8, Rhino, Spidermonkey
at least), so Duktape uses the real world behavior by default.  The strict standards compliant
behavior can be enabled with the feature option <code>DUK_OPT_NO_NONSTD_ARRAY_CONCAT_TRAILER</code>.</p>

<h2>Array.prototype.map() trailing non-existent elements</h2>

<p>Similar issue as with <code>Array.prototype.concat()</code>, see
<a href="https://github.com/svaarala/duktape/blob/master/tests/ecmascript/test-bi-array-proto-map-nonstd-trailing.js">test-bi-array-proto-map-nonstd-trailing.js</a>.  The strict standards compliant behavior can be
enabled with the feature option <code>DUK_OPT_NO_NONSTD_ARRAY_MAP_TRAILER</code>.</p>

<h2>Setter/getter key argument</h2>

<p>Ecmascript standard behavior is that setters and getters are not given
the name of the property being accessed.  This prevents reusing a single
setter or a getter for multiple properties; separate functions are needed
for each property which is sometimes inconvenient and wastes memory.</p>

<p>Duktape provides the property key name as a non-standard additional
argument to setter and getter functions.  See
<a href="https://github.com/svaarala/duktape/blob/master/tests/ecmascript/test-dev-nonstd-setget-key-argument.js">test-dev-nonstd-setget-key-argument.js</a>
and <a href="#propertyvirtualization">Property virtualization</a>
for more discussion.  The strict standards compliant behavior can be enabled
with the feature option <code>DUK_OPT_NO_NONSTD_ACCESSOR_KEY_ARGUMENT</code>.</p>

<h2>Object.setPrototypeOf and Object.prototype.__proto__ (ES6)</h2>

<p>See <a href="#es6-proto">Object.setPrototypeOf and Object.prototype.__proto__</a>.</p>

<h2>Proxy object (ES6)</h2>

<p>See <a href="#es6-proxy">Proxy object (subset)</a>.</p>

<h2>JSON.stringify() escapes U+2028 and U+2029</h2>

<p><code>JSON.stringify()</code> standard behavior is to output U+2028 and
U+2029 without escaping.  This leads to counterintuitive behavior when the
output is used in a web page or parsed with <code>eval()</code>: the U+2028
and U+2029 characters are considered line terminators which leads to a syntax
error (unterminated string).  Duktape escapes U+2028 and U+2029 by default
to avoid this issue; you can turn on the compliant behavior with the feature
option <code>DUK_OPT_NO_NONSTD_JSON_ESC_U2028_U2029</code>.</p>

<h2>String.fromCharCode() accepts 32-bit codepoints</h2>

<p><code>String.fromCharCode()</code> standard behavior is to use ToUInt16()
coercion for codepoint values.  Duktape uses ToUint32() by default to better
support non-BMP strings.  You can force the compliant behavior with the
feature option <code>DUK_OPT_NO_NONSTD_STRING_FROMCHARCODE_32BIT</code>.</p>

<h2>Array instance numeric index writes</h2>

<p>By default Duktape provides a fast path for writing to Array instances.
The fast path is active when numeric indices are used (e.g. <code>arr[7] = 'foo'</code>)
and a few internal conditions are met.  When the fast path is taken, Duktape
doesn't check Array.prototype for conflicting properties (these are very rare in
practical code), which makes common array writes faster.  The behavior is
non-compliant, but there's no outward difference unless Array.prototype has
properties with numeric keys.  You can turn on the compliant behavior with
the feature option <code>DUK_OPT_NO_NONSTD_ARRAY_WRITE</code>.  See the
following for more details on the fast path behavior:
<a href="https://github.com/svaarala/duktape/blob/master/tests/ecmascript/test-misc-array-fast-write.js">test-misc-array-fast-write.js</a>.</p>

<h2 id="typedarray-custombehavior">TypedArray binding</h2>

<p>Duktape provides a Khronos/ES6 TypedArray binding.  The scope of the
binding is the Khronos specification, not the full ES6 specification.
There are some differences to Khronos/ES6, including:</p>
<ul>
<li>Interoperability with other buffer types: Duktape.Buffer or Node.js
    Buffer is allowed wherever an ArrayBuffer or a view object would be
    allowed.</li>
<li>Small differences in argument coercion e.g. for offset and length
    values.</li>
<li>ArrayBuffer has numeric indices (behaving like Uint8Array), and
    additional <code>length</code> (matching <code>byteLength</code>),
    <code>byteOffset</code> (= 0), and <code>BYTES_PER_ELEMENT</code> (= 1)
    properties.</li>
<li>TypedArray <code>subarray()</code> returns a typed array object whose
    internal prototype is copied from the argument rather than being set
    to the default prototype for that view, such as <code>Uint8Array.prototype</code>.</li>
</ul>

<h2 id="nodejsbuffer-custombehavior">Node.js Buffer binding</h2>

<p>Duktape provides a Node.js-like <code>Buffer</code> binding.  There are
some differences between the Node.js behavior and Duktape behavior.  These
differences include:</p>
<ul>
<li>Interoperability with other buffer types: Duktape.Buffer, ArrayBuffer,
    DataView, or a typed array (Uint8Array etc) is allowed wherever a Node.js
    Buffer would be allowed.</li>
<li>Buffer data is always zeroed on allocation and when concatenation
    <code>totalLength</code> exceeds combined size of input buffers.</li>
<li>Read/write offset and length arguments are always validated to ensure
    memory safe behavior, even when <code>noAssert</code> is true.  Failed
    reads return NaN and failed writes return 0.</li>
<li>Partial read/writes are never done: if a read/write is partially
    outside the valid buffer, it is rejected.</li>
<li>Small differences in argument coercion for e.g. offset and length
    values.</li>
<li>Write calls coerce their value argument like TypedArray, e.g. when
    writing 0x100 using <code>writeUInt8()</code> silently coerces to
    0x00 rather than throwing a TypeError.</li>
<li>Duktape only supports the <code>utf8</code> encoding (and accepts no
    spelling variants).  Most API calls ignore an encoding argument, and
    use the Duktape internal string representation (CESU-8 / extended UTF-8)
    for string-to-buffer conversion.</li>
<li>Node.js Buffer has additional <code>byteLength</code> (matching
    <code>length</code>), <code>byteOffset</code> (= 0), and
    <code>BYTES_PER_ELEMENT</code> (= 1) properties.</li>
<li>Node.js Buffer internal class is "Buffer" so
    <code>Object.prototype.toString.call(buf)</code> outputs
    <code>[object Buffer]</code> rather than <code>[object Object]</code>.</li>
</ul>
